#pragma once

#include <optix.h>
#include <optixu/optixu_math_namespace.h>
#include <optixu/optixu_vector_types.h>

#include "commonStructs.h"

using namespace optix;
rtDeclareVariable(int,  nCIESamples, , ) = 471;
rtDeclareVariable(int,  nRGB2SpectSamples, , ) = 32;
rtDeclareVariable(float,  yint, , );

rtDeclareVariable(float3,        X, , );
rtDeclareVariable(float3,        Y, , );
rtDeclareVariable(float3,        Z, , );
 
rtBuffer<float> CIE_X, CIE_Y, CIE_Z;
rtBuffer<float> RGB2SpectLambda, RGBRefl2SpectWhite, RGBRefl2SpectCyan, RGBRefl2SpectMagenta, RGBRefl2SpectYellow,RGBRefl2SpectRed, RGBRefl2SpectGreen, RGBRefl2SpectBlue;
rtBuffer<float> RGBIllum2SpectWhite, RGBIllum2SpectCyan, RGBIllum2SpectMagenta, RGBIllum2SpectYellow, RGBIllum2SpectRed, RGBIllum2SpectGreen, RGBIllum2SpectBlue;


__device__ __inline__ void XYZToRGB(const float3& xyz, float3& rgb) {
    rgb.x =  3.240479f*xyz.x - 1.537150f*xyz.y - 0.498535f*xyz.z;
    rgb.y = -0.969256f*xyz.x + 1.875991f*xyz.y + 0.041556f*xyz.z;
    rgb.z =  0.055648f*xyz.x - 0.204043f*xyz.y + 1.057311f*xyz.z;
}



__device__ __inline__ void RGBToXYZ(const float3& rgb, float3& xyz) {
    xyz.x = 0.412453f*rgb.x + 0.357580f*rgb.y + 0.180423f*rgb.z;
    xyz.y = 0.212671f*rgb.x + 0.715160f*rgb.y + 0.072169f*rgb.z;
    xyz.z = 0.019334f*rgb.x + 0.119193f*rgb.y + 0.950227f*rgb.z;
} 

__device__ __inline__ float3 spectrumToRgb(const float[8] c)
{
	float3 xyz= make_float3(0);
	float3 result = make_float3(0);
	for(int i=0;i<8;i++) 
	{
		xyz.x += c[i]*X[i];
		xyz.y += c[i]*Y[i];
		xyz.z += c[i]*Z[i];
	}
	xyz /= yint;
	XYZToRGB(xyz, result);
	return result;
}

__device__ __inline__ float[8] rgbToReflSpectrum(const float3& c)
{
	 float[8] r;

	 if (rgb.x <= rgb.y && rgb.x <= rgb.z) {
            // Compute reflectance _SampledSpectrum_ with _rgb.x_ as minimum
            r += rgb.x * rgbRefl2SpectWhite;
            if (rgb.y <= rgb.z) {
                r += (rgb.y - rgb.x) * rgbRefl2SpectCyan;
                r += (rgb.z - rgb.y) * rgbRefl2SpectBlue;
            }
            else {
                r += (rgb.z - rgb.x) * rgbRefl2SpectCyan;
                r += (rgb.y - rgb.z) * rgbRefl2SpectGreen;
            }
        }
        else if (rgb.y <= rgb.x && rgb.y <= rgb.z) {
            // Compute reflectance _SampledSpectrum_ with _rgb.y_ as minimum
            r += rgb.y * rgbRefl2SpectWhite;
            if (rgb.x <= rgb.z) {
                r += (rgb.x - rgb.y) * rgbRefl2SpectMagenta;
                r += (rgb.z - rgb.x) * rgbRefl2SpectBlue;
            }
            else {
                r += (rgb.z - rgb.y) * rgbRefl2SpectMagenta;
                r += (rgb.x - rgb.z) * rgbRefl2SpectRed;
            }
        }
        else {
            // Compute reflectance _SampledSpectrum_ with _rgb.z_ as minimum
            r += rgb.z * rgbRefl2SpectWhite;
            if (rgb.x <= rgb.y) {
                r += (rgb.x - rgb.z) * rgbRefl2SpectYellow;
                r += (rgb.y - rgb.x) * rgbRefl2SpectGreen;
            }
            else {
                r += (rgb.y - rgb.z) * rgbRefl2SpectYellow;
                r += (rgb.x - rgb.y) * rgbRefl2SpectRed;
            }
        }
        r *= .94;
}

__device__ __inline__ void mul(float[8]& c, const float v)
{
	for(int i=0;i<8;i++)
		c[i]*=v;
}

__device__ __inline__ void sum(float[8]& c, const float v)
{
	for(int i=0;i<8;i++)
		c[i]+=v;
}
